package com.mxpio.erp.workshop.controller;

import cn.hutool.core.collection.CollectionUtil;
import com.mxpio.erp.common.enums.OrderEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.jpa.model.Order;
import com.mxpio.erp.common.order.policy.OrderContext;
import com.mxpio.erp.common.order.service.OrderLineService;
import com.mxpio.erp.common.order.service.OrderService;
import com.mxpio.erp.common.quality.entity.MoInQualityOrder;
import com.mxpio.erp.common.quality.entity.MoInQualityOrderLine;
import com.mxpio.erp.common.quality.entity.OoInQualityOrder;
import com.mxpio.erp.common.quality.entity.OoInQualityOrderLine;
import com.mxpio.erp.common.vo.NoticeClassVo;
import com.mxpio.erp.common.vo.OrderExcuteVo;
import com.mxpio.erp.common.workshop.entity.*;
import com.mxpio.erp.common.workshop.service.OutsourceOrderItemService;
import com.mxpio.erp.common.workshop.service.OutsourceOrderService;
import com.mxpio.erp.workshop.policy.impl.*;
import com.mxpioframework.common.vo.Result;
import com.mxpioframework.jpa.policy.CrudContext;
import com.mxpioframework.jpa.policy.impl.SmartCrudPolicyAdapter;
import com.mxpioframework.jpa.query.Criteria;
import com.mxpioframework.jpa.query.Operator;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Tag(name = "OutsourceOrderController", description = "委外订单接口")
@RestController
@RequestMapping("/erp/workshop/oo/")
public class OutsourceOrderController {

	@Autowired
	private OutsourceOrderService outsourceOrderService;
	
	@Autowired
	private OutsourceOrderItemService outsourceOrderItemService;
	
	@Autowired
	private OrderService bizOrderService;
	
	@Autowired
    private OutsourceOrderSavePolicy outsourceOrderSavePolicy;

    @Autowired
    private OrderLineService orderLineService;
	@GetMapping("list")
    @Operation(summary = "委外订单列表", description = "获取委外订单列表（无分页）", method = "GET")
    public Result<List<OutsourceOrder>> list(@Parameter(description="查询参数") Criteria criteria) throws UnsupportedEncodingException {
        List<OutsourceOrder> manufactureOrders = outsourceOrderService.list(OutsourceOrder.class, criteria);
        return Result.OK(manufactureOrders);
    }

    @GetMapping("page")
    @Operation(summary = "委外订单列表", description = "获取委外订单列表（分页）", method = "GET")
    public Result<Page<OutsourceOrder>> page(@Parameter(description="查询参数") Criteria criteria, @Parameter(description="每页条数")Integer pageSize, @Parameter(description="页号")Integer pageNo) throws UnsupportedEncodingException {
        Pageable page = PageRequest.of(pageNo-1, pageSize);
        Page<OutsourceOrder> pageResult = outsourceOrderService.listPage(OutsourceOrder.class, page, criteria);
        //outsourceOrderService.handleManufactureOrderQuantity(pageResult.getContent(), OrderEnums.AccessType.IN.getCode());
        NoticeClassVo moInNoticeClazz = new NoticeClassVo(OoInNotice.class, OoInNoticeLine.class);//委外入库通知单
        NoticeClassVo moInQualityOrderClazz = new NoticeClassVo(OoInQualityOrder.class, OoInQualityOrderLine.class);//委外入库检验单
        List<NoticeClassVo> noticeClassVos = new ArrayList<>();
        noticeClassVos.add(moInNoticeClazz);
        noticeClassVos.add(moInQualityOrderClazz);
        bizOrderService.handleBizOrderQuantity(pageResult.getContent(),noticeClassVos,OrderEnums.AccessType.IN.getCode());
        return Result.OK(pageResult);
    }

    @GetMapping("list/{bizNo}")
    @Operation(summary = "根据bizNo获取委外订单", description = "根据bizNo获取委外订单", method = "GET")
    public Result<OutsourceOrder> getById(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
        OutsourceOrder outsourceOrder = outsourceOrderService.getById(OutsourceOrder.class, bizNo);
        return Result.OK(outsourceOrder);
    }

    @RequestMapping("save")
    @Operation(summary = "保存委外订单", description = "保存委外订单")
    public Result<OutsourceOrder> save(@RequestBody OutsourceOrder outsourceOrder) {
        String orderType = outsourceOrder.getOrderType();
        if (StringUtils.equals("03",orderType)){//03 返工订单
            outsourceOrderService.save(outsourceOrder,new SmartCrudPolicyAdapter(){
                @Override
                public boolean beforeInsert(CrudContext context) {
                    Object o = context.getEntity();
                    if(o instanceof OutsourceOrderItem){
                        OutsourceOrder order = context.getParent();
                        ((OutsourceOrderItem) o).setBizNo(order.getBizNo());
                    }
                    return true;
                }
            });
            return Result.OK(outsourceOrder);
        }else{
            outsourceOrderService.save(outsourceOrder,outsourceOrderSavePolicy);
            Criteria c = Criteria.create().addCriterion("bizNo", Operator.EQ, outsourceOrder.getBizNo());
            outsourceOrder.setOutsourceOrderItems(outsourceOrderItemService.list(OutsourceOrderItem.class, c));
            return Result.OK(outsourceOrder);
        }
    }
    
    @RequestMapping("excute/{bizNo}")
    @Operation(summary = "委外订单报完工", description = "委外订单报完工")
    public Result<Object> excute(@PathVariable(name = "bizNo", required = true) String bizNo, 
    		@RequestBody OrderExcuteVo orderExcuteVo) {
        if (orderExcuteVo==null|| CollectionUtil.isEmpty(orderExcuteVo.getLines())){
            return Result.error("提交参数对象为空！");
        }
    	OutsourceOrder order = outsourceOrderService.getById(OutsourceOrder.class, bizNo);
        if (order.getOrderType().equals("03")){//返工类型订单
            orderExcuteVo.getLines().forEach(m->{
                m.setInQualityControl("1");//指定需要生成检验单
            });
        }else{
            //普通和计划订单，成品完工入库根据物料组或物料设置的是否入库检来控制是否走验收单
            bizOrderService.handleInQualityControl(orderExcuteVo.getLines());
        }
    	OrderContext context = bizOrderService.execute(order, new DefaultExecuteOoInPolicy(orderExcuteVo.getLines(),bizOrderService));
    	if(CollectionUtils.isEmpty(context.getErrors())) {
    		return Result.OK();
    	}else {
    		return Result.error(context.getErrors().get(0));
    	}
    }
    
    @RequestMapping("excute/{type}/{bizNo}")
    @Operation(summary = "委外领/退/补料", description = "委外领/退/补料")
    public Result<Object> excute(@PathVariable(name = "bizNo", required = true) String bizNo, 
    		@PathVariable(name = "type", required = true) @Parameter(description = "18:退料；46：补料；46：领料") String type,
    		@RequestBody OrderExcuteVo orderExcuteVo) {
    	OutsourceOrder order = outsourceOrderService.getById(OutsourceOrder.class, bizNo);
    	if (order.getCloseStatus().equals(OrderStatusEnums.CloseStatus.CLOSED.getStatusCode())){
            return Result.error("关闭状态不能执行该操作");
        }
        if (order.getOrderStatus().equals(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode())){
            return Result.error("已完成状态不能执行该操作");
        }
        if (order.getOrderStatus().equals(OrderStatusEnums.OrderStatus.CLEARED.getStatusCode())){
            return Result.error("已结算状态不能执行该操作");
        }
    	OrderContext context = bizOrderService.execute(order, new DefaultPickingOoPolicy(orderExcuteVo.getLines(), type,orderLineService));
    	if(CollectionUtils.isEmpty(context.getErrors())) {
    		return Result.OK();
    	}else {
    		return Result.error(context.getErrors().get(0));
    	}
    }

    @RequestMapping("excuteScrap/{bizNo}")
    @Operation(summary = "委外不良品退料", description = "委外不良品退料")
    public Result<Object> excuteScrap(@PathVariable(name = "bizNo", required = true) String bizNo,
                                 @RequestBody OrderExcuteVo orderExcuteVo) {
        OutsourceOrder order = outsourceOrderService.getById(OutsourceOrder.class, bizNo);
        if (order.getCloseStatus().equals(OrderStatusEnums.CloseStatus.CLOSED.getStatusCode())){
            return Result.error("关闭状态不能执行该操作");
        }
        if (order.getOrderStatus().equals(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode())){
            return Result.error("已完成状态不能执行该操作");
        }
        if (order.getOrderStatus().equals(OrderStatusEnums.OrderStatus.CLEARED.getStatusCode())){
            return Result.error("已结算状态不能执行该操作");
        }
        OrderContext context = bizOrderService.execute(order, new DefaultOODefectivePolicy(orderExcuteVo.getLines(),orderLineService));
        if(CollectionUtils.isEmpty(context.getErrors())) {
            return Result.OK();
        }else {
            return Result.error(context.getErrors().get(0));
        }
    }
    @DeleteMapping("remove/{bizNos}")
    @Operation(summary = "根据bizNo删除委外订单", description = "删除委外订单", method = "DELETE")
    @Transactional(readOnly = false)
    public Result<OutsourceOrder> remove(@Parameter(description="业务单号，多个单号用英文逗号分隔") @PathVariable(name = "bizNos", required = true) String bizNos) {
        String[] bizNo = bizNos.split(",");
        for(String key : bizNo){
        	Criteria c = Criteria.create().addCriterion("bizNo", Operator.EQ, key);
        	outsourceOrderItemService.removeBatch(OutsourceOrderItem.class, c);
            outsourceOrderService.delete(OutsourceOrder.class, key);
        }
        return Result.OK();
    }
    
    @PutMapping("submit/{bizNo}")
    @Operation(summary = "提交委外订单", description = "提交委外订单", method = "PUT")
    public Result<? extends Order> submit(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
    	OutsourceOrder so = outsourceOrderService.getById(OutsourceOrder.class,bizNo);
    	if(so.getBpmnStatus().equals(OrderStatusEnums.BpmnStatus.CREATE.getStatusCode())){
    		return bizOrderService.updateBpmnStatus(so, OrderStatusEnums.BpmnStatus.AUDITING);
    	}else {
    		return Result.error("非开立状态不可提交");
		}
    }

    @PutMapping("audit/{bizNo}")
    @Operation(summary = "审核委外订单", description = "审核委外订单", method = "PUT")
    public Result<? extends Order> audit(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
    	OutsourceOrder so = outsourceOrderService.getById(OutsourceOrder.class,bizNo);
    	if(so.getBpmnStatus().equals(OrderStatusEnums.BpmnStatus.AUDITING.getStatusCode())){
    		return bizOrderService.updateBpmnStatus(so, OrderStatusEnums.BpmnStatus.AUDITED);
    	}else {
    		return Result.error("非审核中状态不可审核");
		}
    }

    @PutMapping("abandon/{bizNo}")
    @Operation(summary = "弃审委外订单", description = "弃审委外订单", method = "PUT")
    public Result<? extends Order> abandon(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
    	OutsourceOrder mo = outsourceOrderService.getById(OutsourceOrder.class,bizNo);
    	if(mo.getBpmnStatus().equals(OrderStatusEnums.BpmnStatus.AUDITING.getStatusCode())||mo.getBpmnStatus().equals(OrderStatusEnums.BpmnStatus.AUDITED.getStatusCode())){
    		return bizOrderService.updateBpmnStatus(mo, OrderStatusEnums.BpmnStatus.CREATE);
    	}else{
    		return Result.error("非审核中、已审核状态不可审核");
    	}
    }

    /*@PutMapping("export/{bizNos}")
    @Operation(summary = "导出委外订单", description = "导出委外订单", method = "PUT")
    public Result<OutsourceOrder> export(@Parameter(description="业务单号，多个单号用英文逗号分隔") @PathVariable(name = "bizNos", required = true) String bizNos) {
        //TODO:导出逻辑
        return Result.OK();
    }*/

    @PutMapping("open/{bizNo}")
    @Operation(summary = "打开委外订单", description = "打开委外订单", method = "PUT")
    public Result<? extends Order> open(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
    	OutsourceOrder mo = outsourceOrderService.getById(OutsourceOrder.class,bizNo);
    	return bizOrderService.updateCloseStatus(mo, OrderStatusEnums.CloseStatus.OPEN);
    }

    @PutMapping("close/{bizNo}")
    @Operation(summary = "关闭委外订单", description = "关闭委外订单", method = "PUT")
    public Result<? extends Order> close(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
    	OutsourceOrder mo = outsourceOrderService.getById(OutsourceOrder.class,bizNo);
    	return bizOrderService.updateCloseStatus(mo, OrderStatusEnums.CloseStatus.CLOSED);
    }
    
    @PutMapping("order/{bizNo}")
    @Operation(summary = "下达委外订单", description = "下达委外订单", method = "PUT")
    public Result<? extends Order> order(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
    	OutsourceOrder mo = outsourceOrderService.getById(OutsourceOrder.class,bizNo);
        List<OutsourceOrderItem> itemList = outsourceOrderItemService.listByBizNo(bizNo);
    	if(OrderStatusEnums.OrderStatus.PLAN.getStatusCode().equals(mo.getOrderStatus())){
    		return bizOrderService.updateOrderStatus(mo, OrderStatusEnums.OrderStatus.ORDERED);
    	}else{
    		return Result.error("非计划状态不可下达");
    	}
    }
    
    @PutMapping("clear/{bizNo}")
    @Operation(summary = "结算委外订单", description = "结算委外订单", method = "PUT")
    public Result<? extends Order> clear(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
    	OutsourceOrder mo = outsourceOrderService.getById(OutsourceOrder.class,bizNo);
    	if(OrderStatusEnums.OrderStatus.FINISHED.getStatusCode().equals(mo.getOrderStatus())){
            //校验明细 是否存在未执行完毕的退料、补料、领料记录
            Criteria c = Criteria.create().addCriterion("bizNo", Operator.EQ, bizNo);
            List<OutsourceOrderItem> outsourceOrderItems = outsourceOrderItemService.list(OutsourceOrderItem.class, c);
            if (outsourceOrderItems.size()>0){
                //计算数量字段
                NoticeClassVo pickingNoticeClazz = new NoticeClassVo(MoPickingNotice.class, MoPickingNoticeLine.class);//领/补/退
                NoticeClassVo defectiveProductsNoticeClazz = new NoticeClassVo(DefectiveProductsNotice.class, DefectiveProductsNoticeLine.class);//不良品退料
                List<NoticeClassVo> noticeClassVos = new ArrayList<>();
                noticeClassVos.add(pickingNoticeClazz);
                noticeClassVos.add(defectiveProductsNoticeClazz);
                orderLineService.handleBizOrderLineQuantity(outsourceOrderItems, noticeClassVos, OrderEnums.AccessType.OUT.getCode());
                for (OutsourceOrderItem orderItem:outsourceOrderItems){
                    if ((orderItem.getPlanQuantity()!=null &&orderItem.getPlanQuantity().compareTo(BigDecimal.ZERO)!=0)
                            || (orderItem.getPlanRejectQuantity()!=null && orderItem.getPlanRejectQuantity().compareTo(BigDecimal.ZERO)!=0)){
                        return Result.error("订单行存在未执行完毕的领、退、补料记录，无法结算订单。");
                    }
                }
            }
            //校验订单主单
            List<OutsourceOrder> orderList = new ArrayList<>();
            orderList.add(mo);
            NoticeClassVo moInNoticeClazz = new NoticeClassVo(OoInNotice.class, OoInNoticeLine.class);//委外入库通知单
            NoticeClassVo moInQualityOrderClazz = new NoticeClassVo(OoInQualityOrder.class, OoInQualityOrderLine.class);//委外入库检验单
            List<NoticeClassVo> noticeClassVos = new ArrayList<>();
            noticeClassVos.add(moInNoticeClazz);
            noticeClassVos.add(moInQualityOrderClazz);
            bizOrderService.handleBizOrderQuantity(orderList,noticeClassVos,OrderEnums.AccessType.IN.getCode());
            if (mo.getPlanQuantity()!=null && mo.getPlanQuantity().compareTo(BigDecimal.ZERO) !=0){
                return Result.error("订单存在未执行完成的产品入库记录，无法结算订单。");
            }
    		return bizOrderService.updateOrderStatus(mo, OrderStatusEnums.OrderStatus.CLEARED);
    	}else{
    		return Result.error("非完成状态不可结算");
    	}
    }
    
    @PutMapping("finish/{bizNo}")
    @Operation(summary = "委外订单强制完工", description = "委外订单强制完工", method = "PUT")
    public Result<? extends Order> finish(@Parameter(description="业务单号") @PathVariable(name = "bizNo", required = true) String bizNo) {
    	OutsourceOrder mo = outsourceOrderService.getById(OutsourceOrder.class,bizNo);
    	if(OrderStatusEnums.OrderStatus.PROCESSING.getStatusCode().equals(mo.getOrderStatus())){
    		return bizOrderService.updateOrderStatus(mo, OrderStatusEnums.OrderStatus.FINISHED);
    	}else{
    		return Result.error("非执行状态不可报完工");
    	}
    }
}
